﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Drawing;

using ProyectoSI;
using ProyectoSI.Excepciones;


namespace VisionEstereo.SegmentacionColores
{
    ///</summary>
    /// Clase que contiene el algoritmo de segmentacion
    /// de colores mediante cuantización vetorial.
    ///</summary>
    public unsafe  class AlgCuantizacionVectorial
    {
        ///<summary>
        /// Método encargado de obtener la segmentación de colores a partir de la
        /// imagen pasada como parámetro.
        ///</summary>
        ///<param name="imagen">Imagen</param>
        ///<param name="umbral">Umbral de discretización de colores del algoritmo</param>
        ///<returns>Bitmap con la imagen segmentada en colores</returns>
        ///

        static ArrayList listaClases;
        static PixelDato pixel;
        //---matriz de clases-----///
        static int[,] matrizClase;
        static int claseN;

        public  Bitmap EjecutarAlgCuantizacionVectorial(Bitmap imagen, int umbral, bool coloresAleatorios)
        {
            Clase clase, obj, objMin;
            BitmapFast.PixelData* color; //contexto no seguro (unsafe)

            //---inicializo la matriz de clases---///
            matrizClase = new int[imagen.Size.Width, imagen.Size.Height];
            PixelDato obj2;
            double distancia, minimo;
            int  claseMinima;
            Bitmap imagenSalida;
            int contador = 0;

            
            imagenSalida = new Bitmap(imagen,imagen.Size);
            BitmapFast imagenAccesible = new BitmapFast(imagen);
            imagenAccesible.LockBitmap();
            listaClases = new ArrayList();
            objMin = new Clase();
            

            //recorremos imagen pixel a pixel
            for (int i = 0; i < imagen.Size.Width; i++)
                for (int j = 0;  j < imagen.Size.Height; j++)
                    {
                        color = imagenAccesible.PixelAt(i, j);  
                        pixel = new PixelDato(color->blue,color->green, color->red,i,j);
                        //si esta vacía, creamos la primera clase
                        if (listaClases.Count == 0)
                        {
                            clase = new Clase(pixel);
                            listaClases.Add(clase);
                        }
                        //si no, calculamos distancias a los centros de las clases y nos quedamos con el mínimo
                        else
                        {
                            minimo = Int16.MaxValue;//infinito
                            claseN = 0;
                            claseMinima = 0;
                            IEnumerator objMyEnum = listaClases.GetEnumerator();
                           
                            while (objMyEnum.MoveNext())
                            {
                                obj = (Clase)objMyEnum.Current;
                                distancia = distanciaEuclidea(obj.getCentro(), pixel);
                                if (distancia < minimo)
                                {
                                    minimo = distancia;
                                    claseMinima = claseN;                                  
                                    objMin = obj;
                                }
                                claseN++;
                            }
                            //si el minimo es mayor que el umbral, creamos nueva clase
                            if (minimo > umbral)
                            {
                                clase = new Clase(pixel);
                                listaClases.Add(clase);
                            }
                            //si no, lo añadimos a la clase cuya distancia es menor
                            else
                            {
                                objMin.addElement(pixel);
                                listaClases.RemoveAt(claseMinima);
                                listaClases.Insert(claseMinima, objMin);
                                contador++;
                            }
                        }                        
                    }
            imagenAccesible.UnlockBitmap();

            //ahora tenemos la matrizSalida con los pixeles asociados a cada clase
            //y vamos a construir el BitMap de salida con los colores asignados
            IEnumerator objMyEnum2 = listaClases.GetEnumerator();
            
            Color color_aux;

            // variable para saber a qué clase pertenece---//
            int queClase=0;

            byte aleaR, aleaG, aleaB;
            ArrayList listaClasesAleatoria = new ArrayList(listaClases);
            Random r = new Random();
            while (objMyEnum2.MoveNext())
            {
                obj = (Clase)objMyEnum2.Current;
                if(!coloresAleatorios)
                    color_aux = System.Drawing.Color.FromArgb(obj.getCentro().Red, obj.getCentro().Green, obj.getCentro().Blue);
                else{
                    aleaR = (byte) r.Next(0, 256);
                    aleaG = (byte) r.Next(0, 256);
                    aleaB = (byte) r.Next(0, 256);
                    color_aux = System.Drawing.Color.FromArgb(aleaR, aleaG, aleaB);

                    /*modificamos el RGB de la clase con el color aleatorio obtenido*/                  
                    PixelDato centro = obj.getCentro();
                    PixelDato pix = new PixelDato(color_aux.B, color_aux.G, color_aux.R, centro.getPosX(), centro.getPosY());
                    Clase claseNueva = new Clase(pix);
                    listaClasesAleatoria.Remove(obj);
                    listaClasesAleatoria.Add(claseNueva);
                    }
                ArrayList lista = obj.getLista();
                IEnumerator objMyEnum3 = lista.GetEnumerator() ;//listaClases.GetEnumerator();
                //--Construimos también la matriz de clases---//

                queClase++;
                while (objMyEnum3.MoveNext())
                {
                    obj2 = (PixelDato)objMyEnum3.Current;
                  //----matriz de clases ------------//
                    matrizClase[obj2.getPosX(), obj2.getPosY()]=queClase;
                    
                  //---------------------------------------------------//
                    imagenSalida.SetPixel(obj2.getPosX(),obj2.getPosY(),color_aux);
                }
                listaClases = listaClasesAleatoria; //Para los colores aleatorios
                
            }
            
            return imagenSalida;
            
        }

        public int[,] dameMatrizClases()
        {
            return matrizClase;
        }
        public int dameNumeroClases()
        {
            return claseN;
        }
        public ArrayList dameListaClases()
        {
            return listaClases;
        }
        private static double distanciaEuclidea(PixelDato centro, PixelDato pixel)
        {
            return Math.Sqrt((Math.Abs(centro.Blue - pixel.Blue) ^ 2) + (Math.Abs(centro.Red - pixel.Red) ^ 2) + (Math.Abs(centro.Green - pixel.Green) ^ 2));
        }


    }
}
